package com.kclm.se.io;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.LinkedList;

/*****
 * @author yejf
 * @since JDK8.0
 * @CreateDate 2020年8月27日 下午5:22:17 
 * @description 此类用来描述了非阻塞的IO操作，调用操作系统的非阻塞行为
 * 注：
 *  在java nio的API中，如果在网络通道中
 *
 */
public class SocketNIO {

	public static void main(String[] args) throws Exception {
		//1.定义一个集合来存储所有的客户端Socket通道
		LinkedList<SocketChannel> clients = new LinkedList<>();
		
		//2. 服务端开启监听, 接收客户端的连接
		ServerSocketChannel ssc = ServerSocketChannel.open();
		//2.1 把ServerSocketChannel绑定到9292端口
		final int port = 9292;
		ssc.bind(new InetSocketAddress(port));
		//2.2 重点：申请操作系统的非阻塞IO调用
		ssc.configureBlocking(false);  //NONBLOCKING!!! 
		
		//开始循环等待客户端的连接
		while(true) {
			//调用了内核，1.当没有客户端连接进料来的时候,BIO的进候会一直阻塞，但是在NIO中，不卡着，返回-1, 
			//对应JAVA的程序，返回null, 如果有客户端连接进来，accept返回的是这个客户端的fd,比如5, 对应的java程序就是SocketChannel
			SocketChannel client = ssc.accept(); //不会阻塞 
			//
			if(client == null) {
				//System.out.println("null...");
			} else {
				client.configureBlocking(false); //重点，客户端的socket也要设置为 非阻塞模式
				int c_port = client.socket().getPort();
				//
				System.out.println("Client port: "+c_port);
				//
				clients.add(client);
			}
			
			//开始读取, 通过ByteBuffer 来做缓存区
			ByteBuffer buffer = ByteBuffer.allocate(4096); //4K
			//遍历已经连接进来的客户端，能不能读写数据
			for(SocketChannel c : clients) {  //串行化！！！！ 多线程
				// 从 socketchannel中读取数据到 buffer中， 每调用一次，都会造成系统调用[accept, read]
				//有10000个连接进来，就会发生10000次的系统调用【accept, read】，而实际上这10000次中只有几次是有数据的，其它都是空跑
				int num = c.read(buffer); // >0 -1, 0  不会阻塞
				// 如何改造成 多路复用呢？ 【注：多路复用还是 同步IO模型, EPOLL也是同步IO模型】
				if(num > 0) {
					//说明读到了
					buffer.flip(); //反转: limit = position;  position=0;  mark = -1
					//从缓存中取出数据
					byte[] result = new byte[buffer.limit()];
					buffer.get(result);
					
					//把字节数组转换成  String后，输出
					String resultStr = new String(result);
					System.out.printf("%d : %s\n", c.socket().getPort(), resultStr);
					//清空buffer
					buffer.clear();
				} else {
					//System.out.println(">> 没有读到...: "+num);
				}
			}
			
		}
	}
}
